package com.atom.wyz.new_opengl.util;

import android.opengl.GLES11Ext;
import android.opengl.GLES30;

import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class SimpleShaderHandle {
    public static final String APOSITION = "aPosition";
    public static final String ATEXCOORS = "aTexCoor";
    public static final String ANORMAL = "aNormal";
    public static final String ACOLOR = "aColor";

    public static final String UMVPMATRIX = "uMVPMatrix";
    public static final String UVPMATRIX = "uVPMatrix";
    public static final String UMMATRIX = "uMMatrix";
    public static final String UTMatrix = "uTMatrix";

    public static final String ULIGHT = "uLight";
    public static final String UCAMERA = "uCamera";

    public static final String UTEXTURE = "sTexture";

    private int mProgram;


    private Map<String, Integer> handleMaps;

    public SimpleShaderHandle(int program) {
        this(program, true);
    }

    public SimpleShaderHandle(int program, boolean isInit) {
        handleMaps = new HashMap<>();
        mProgram = program;
        if (isInit) {
            build();
        }
    }

    public void handleMaps(String handleName, Integer index) {
        // index == - 2  Attrib
        // index == - 3  Uniform
        // index == - 1  default
        handleMaps.put(handleName, index);
    }

    public Integer getHandleIndex(String handleName) {
        return handleMaps.get(handleName);
    }

    public int getProgram() {
        return mProgram;
    }


    public SimpleShaderHandle build() {

        handleMaps.put(UMVPMATRIX, -3);
        handleMaps.put(UMMATRIX, -3);
        handleMaps.put(UTMatrix, -3);
        handleMaps.put(ULIGHT, -3);
        handleMaps.put(UCAMERA, -3);
        handleMaps.put(UTEXTURE, -3);

        handleMaps.put(APOSITION, -2);
        handleMaps.put(ATEXCOORS, -2);
        handleMaps.put(ANORMAL, -2);
        handleMaps.put(ACOLOR, -2);
        Iterator<Map.Entry<String, Integer>> iterator = handleMaps.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> entry = iterator.next();

            Integer handletype = entry.getValue();
            String handleName = entry.getKey();

            if (handletype == null || handleName == null) continue;

            if (handletype == -2) {
                int handle = GLES30.glGetAttribLocation(mProgram, handleName);
                handleMaps.put(handleName, handle);
            } else if (handletype == -3) {
                int handle = GLES30.glGetUniformLocation(mProgram, handleName);
                handleMaps.put(handleName, handle);
            } else {
                handleMaps.put(handleName, -1);
            }
        }

        return this;
    }

    public SimpleShaderHandle program() {
        GLES30.glUseProgram(getProgram());
        return this;
    }

    public SimpleShaderHandle bindVertexHandle(FloatBuffer floatBuffer) {
        if (floatBuffer == null) return this;
        Integer handleIndex = getHandleIndex(APOSITION);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glVertexAttribPointer(
                handleIndex,
                3,
                GLES30.GL_FLOAT,
                false,
                3 * 4,
                floatBuffer
        );
        return this;
    }

    public SimpleShaderHandle bindTexCoorsHandle(FloatBuffer floatBuffer) {
        if (floatBuffer == null) return this;
        Integer handleIndex = getHandleIndex(ATEXCOORS);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glVertexAttribPointer(
                handleIndex,
                2,
                GLES30.GL_FLOAT,
                false,
                2 * 4,
                floatBuffer
        );
        return this;
    }

    public SimpleShaderHandle bindNormalHandle(FloatBuffer floatBuffer) {
        if (floatBuffer == null) return this;
        Integer handleIndex = getHandleIndex(ANORMAL);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glVertexAttribPointer(
                handleIndex,
                3,
                GLES30.GL_FLOAT,
                false,
                3 * 4,
                floatBuffer
        );
        return this;
    }

    public SimpleShaderHandle bindColorsHandle(FloatBuffer floatBuffer) {
        if (floatBuffer == null) return this;
        Integer handleIndex = getHandleIndex(ACOLOR);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glVertexAttribPointer(
                handleIndex,
                4,
                GLES30.GL_FLOAT,
                false,
                4 * 4,
                floatBuffer
        );
        return this;
    }

    public SimpleShaderHandle bindMVPMaxtrixHandle() {
        bindMVPMaxtrixHandle(MatrixState.getFinalMatrix());
        return this;
    }

    public SimpleShaderHandle bindMVPMaxtrixHandle(float[] floats) {
        Integer handleIndex = getHandleIndex(UMVPMATRIX);
        if (!checkHandleIsEffect(handleIndex)) return this;
        GLES30.glUniformMatrix4fv(handleIndex, 1, false, floats, 0);
        return this;
    }

    public SimpleShaderHandle bindVPMaxtrixHandle(float[] floats) {
        Integer handleIndex = getHandleIndex(UVPMATRIX);
        if (!checkHandleIsEffect(handleIndex)) return this;
        GLES30.glUniformMatrix4fv(handleIndex, 1, false, floats, 0);
        return this;
    }

    public SimpleShaderHandle bindMMaxtrixHandle() {
        bindMMaxtrixHandle(MatrixState.getMMatrix());
        return this;
    }

    public SimpleShaderHandle bindMMaxtrixHandle(float[] floats) {
        Integer handleIndex = getHandleIndex(UMMATRIX);
        if (!checkHandleIsEffect(handleIndex)) return this;
        GLES30.glUniformMatrix4fv(handleIndex, 1, false, floats, 0);
        return this;
    }

    public SimpleShaderHandle bindTMaxtrixHandle() {
        bindTMaxtrixHandle(MatrixUtils.getOriginalMatrix());

        return this;
    }

    public SimpleShaderHandle bindTMaxtrixHandle(float[] floats) {
        Integer handleIndex = getHandleIndex(UTMatrix);
        if (!checkHandleIsEffect(handleIndex)) return this;
        GLES30.glUniformMatrix4fv(handleIndex, 1, false, floats, 0);
        return this;
    }

    public SimpleShaderHandle bindCameraHandle(FloatBuffer floatBuffer) {
        Integer handleIndex = getHandleIndex(UCAMERA);
        if (!checkHandleIsEffect(handleIndex)) return this;
        if(floatBuffer == null){
            floatBuffer = MatrixState.cameraLocationFB ;
        }
        GLES30.glUniform3fv(handleIndex , 1, floatBuffer);
        return this;
    }

    public SimpleShaderHandle bindLightHandle(FloatBuffer floatBuffer) {
        Integer handleIndex = getHandleIndex(ULIGHT);
        if (!checkHandleIsEffect(handleIndex)) return this;
        if(floatBuffer == null){
            floatBuffer = MatrixState.lightLocationFB ;
        }
        GLES30.glUniform3fv(handleIndex , 1, floatBuffer);
        return this;
    }

    public SimpleShaderHandle bindDefaultTextureHandle(int id) {
        Integer handleIndex = getHandleIndex(UTEXTURE);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id);
        GLES30.glUniform1i(handleIndex, 0);
        return this;
    }

    public SimpleShaderHandle bindDefault3DTextureHandle(int id) {
        Integer handleIndex = getHandleIndex(UTEXTURE);
        if (!checkHandleIsEffect(handleIndex)) return this;
        //GLES30.glActiveTexture(GLES30.GL_TEXTURE_CUBE_MAP);
        GLES30.glEnable(GLES30.GL_TEXTURE_CUBE_MAP);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, id);
        GLES30.glUniform1i(handleIndex, 0);
        return this;
    }

    public SimpleShaderHandle bindDefaultOesTextureHandle(int id) {
        Integer handleIndex = getHandleIndex(UTEXTURE);
        if (!checkHandleIsEffect(handleIndex)) return this;

        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, id);
        GLES30.glUniform1i(handleIndex, 0);
        return this;
    }

    public void run(int mode, int vCount) {
        if (checkHandleIsEffect(getHandleIndex(ANORMAL)))
            GLES30.glEnableVertexAttribArray(getHandleIndex(ANORMAL));
        if (checkHandleIsEffect(getHandleIndex(APOSITION)))
            GLES30.glEnableVertexAttribArray(getHandleIndex(APOSITION));
        if (checkHandleIsEffect(getHandleIndex(ATEXCOORS)))
            GLES30.glEnableVertexAttribArray(getHandleIndex(ATEXCOORS));
        if (checkHandleIsEffect(getHandleIndex(ACOLOR)))
            GLES30.glEnableVertexAttribArray(getHandleIndex(ACOLOR));

        GLES30.glDrawArrays(mode, 0, vCount);

        if (checkHandleIsEffect(getHandleIndex(ANORMAL)))
            GLES30.glDisableVertexAttribArray(getHandleIndex(ANORMAL));
        if (checkHandleIsEffect(getHandleIndex(APOSITION)))
            GLES30.glDisableVertexAttribArray(getHandleIndex(APOSITION));
        if (checkHandleIsEffect(getHandleIndex(ATEXCOORS)))
            GLES30.glDisableVertexAttribArray(getHandleIndex(ATEXCOORS));
        if (checkHandleIsEffect(getHandleIndex(ACOLOR)))
            GLES30.glDisableVertexAttribArray(getHandleIndex(ACOLOR));
    }

    public void destroy() {
        GLES30.glDeleteProgram(mProgram);
    }

    private boolean checkHandleIsEffect(Integer value) {
        return value != null && value >= 0;
    }


}
